package com.piece.mall.service;

import com.piece.core.elasticsearch.support.EsSearchUtil;
import com.piece.core.framework.constant.ExceptionConstants;
import com.piece.core.framework.enums.StatusType;
import com.piece.core.framework.exception.ServerException;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.util.collection.MapUtil;
import com.piece.core.framework.util.object.BeanUtil;
import com.piece.core.jpa.repository.BaseService;
import com.piece.mall.api.dto.SkuEsDTO;
import com.piece.mall.model.*;
import com.piece.mall.repository.PmsSpuInfoRepository;
import com.piece.mall.vo.BoundsVo;
import com.piece.mall.vo.SkuInfoVo;
import com.piece.mall.vo.SpuInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

@Slf4j
@Service
@Transactional
public class PmsSpuInfoService extends BaseService<PmsSpuInfo, String> {

    @Autowired
    private PmsSpuInfoRepository pmsSpuInfoRepository;

    @Resource
    private PmsSpuInfoDescService pmsSpuInfoDescService;

    @Resource
    private PmsSpuImagesService pmsSpuImagesService;

    @Resource
    private PmsProductAttrValueService pmsProductAttrValueService;

    @Resource
    private PmsBrandService pmsBrandService;

    @Resource
    private PmsSkuInfoService pmsSkuInfoService;

    @Resource
    private PmsAttrService pmsAttrService;

    @Resource
    private PmsCategoryService pmsCategoryService;

    @Resource
    private WmsWareSkuService wmsWareSkuService;

    @PostConstruct
    public void initRepository() {
        setRepository(pmsSpuInfoRepository);
    }

    public PmsSpuInfo findBySku(Long skuId) {
        PmsSkuInfo skuInfo = pmsSkuInfoService.findById(skuId);
        PmsSpuInfo spuInfo = findById(skuInfo.getSpuId());
        spuInfo.setBrandName(pmsBrandService.findNameById(spuInfo.getBrandId()));
        return spuInfo;
    }

    /**
     * 保存spu
     */
    public void saveSpuInfo(SpuInfoVo spuInfoVo) throws Exception {
        // 1.保存spu基本信息
        PmsSpuInfo spuInfo = new PmsSpuInfo();
        BeanUtil.copyProperties(spuInfoVo, spuInfo, "catalogPath", "weight", "publishStatus", "decryptImages",
                "spuImages", "bounds", "attrs", "skuInfos");
        Date now = new Date();
        spuInfo.setCreateTime(now);
        spuInfo.setUpdateTime(now);
        this.insert(spuInfo);
        Long spuId = spuInfo.getId();
        spuInfoVo.setId(spuId);

        // 2.保存spu介绍
        PmsSpuInfoDesc desc = new PmsSpuInfoDesc();
        desc.setSpuId(spuId);
        desc.setIntroduce(spuInfoVo.getIntroduce());
        pmsSpuInfoDescService.insert(desc);

        // 3.保存spu图片集
        pmsSpuImagesService.saveSpuImages(spuInfoVo);

        // 4.保存spu基本参数值
        List<PmsAttr> baseAttrs = spuInfoVo.getAttrs();
        pmsProductAttrValueService.saveProductAttrValue(spuId, baseAttrs);

        // 5.保存spu的积分信息 TODO
        BoundsVo bounds = spuInfoVo.getBounds();

        // 6.保存当前spu对应的所有sku信息
        List<SkuInfoVo> skuInfos = spuInfoVo.getSkuInfos();
        pmsSkuInfoService.saveSkuInfo(spuInfo, skuInfos);
    }

    /**
     * 上架商品
     */
    public void publish(Long spuId) {
        // 1.查询sku列表
        List<PmsSkuInfo> skuInfos = pmsSkuInfoService.findBySpu(spuId);

        // 2.查询规格参数
        List<PmsProductAttrValue> attrValues = pmsProductAttrValueService.findBySpuId(spuId);
        Map<Long, PmsProductAttrValue> attrMap = attrValues.stream().collect(Collectors.toMap(key -> key.getAttrId(), val -> val));

        // 3.查询允许被检索的基本属性集合
        List<PmsAttr> attrs = pmsAttrService.findByCondition(MapUtil.builder(new HashMap<String, Object>())
                .put("Q_attrId_L_IN", new ArrayList<>(attrMap.keySet())).build());

        // 4.封装基本属性
        List<SkuEsDTO.Attrs> attrModels = attrs.stream().map(pmsAttr -> {
            SkuEsDTO.Attrs attrModel = new SkuEsDTO.Attrs();
            PmsProductAttrValue attrValue = attrMap.get(pmsAttr.getAttrId());
            attrModel.setAttrId(attrValue.getAttrId());
            attrModel.setAttrName(attrValue.getAttrName());
            attrModel.setAttrValue(attrValue.getAttrValue());
            return attrModel;
        }).collect(Collectors.toList());

        // 5.查询库存
        List<Long> skuIds = skuInfos.stream().map(PmsSkuInfo::getSkuId).collect(Collectors.toList());
        Map<Long, Long> skuStockStatus = wmsWareSkuService.findSkuStocks(skuIds);

        // 6.封装elasticsearch对象
        List<SkuEsDTO> skuEsModels = skuInfos.stream().map(sku -> {
            SkuEsDTO skuEs = new SkuEsDTO();
            BeanUtil.copyProperties(sku, skuEs, "skuName", "skuDesc", "skuSubtitle");
            skuEs.setHasStock(null == skuStockStatus ? true : Convert.toLong(skuStockStatus.get(sku.getSkuId()), 0L) > 0);
            skuEs.setAttrs(attrModels);
            return skuEs;
        }).collect(Collectors.toList());

        // 7.创建索引
        AtomicBoolean result = new AtomicBoolean(true);
        skuEsModels.stream().forEach(model -> {
            try {
                EsSearchUtil.getInstance().insert(model);
            } catch (Exception e) {
                result.set(false);
                log.error(e.getMessage());
            }
        });

        if (result.get()) {
            // 更新商品上架状态
            pmsSpuInfoRepository.executeSql("update pms_spu_info set publish_status = " + StatusType.NORMAL.getCode()
                    + " where id = " + spuId);
        }
    }

    /**
     * 下架商品
     */
    public void cancel(Long spuId) {
        try {
            List<PmsSkuInfo> skuInfos = pmsSkuInfoService.findBySpu(spuId);
            for (PmsSkuInfo skuInfo : skuInfos) {
                EsSearchUtil.getInstance().delete(skuInfo.getSkuId().toString(), SkuEsDTO.class);
            }

            // 更新商品上架状态
            pmsSpuInfoRepository.executeSql("update pms_spu_info set publish_status = " + StatusType.DELETE.getCode()
                    + " where id = " + spuId);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    @Override
    public void delete(Serializable spuId) {
        delete(new Serializable[]{ spuId });
    }

    @Override
    public int delete(Serializable[] spuIds) {
        List<Serializable> collect = Arrays.stream(spuIds).map(id -> {
            Long spuId = Convert.toLong(id);
            // 1.查询sku列表
            List<PmsSkuInfo> skuInfos = pmsSkuInfoService.findBySpu(spuId);
            List<Long> skuIds = skuInfos.stream().map(info -> info.getSkuId()).collect(Collectors.toList());
            // 2.删除库存数据
            wmsWareSkuService.deleteSkuStock(skuIds);
            // 3.删除sku信息
            pmsSkuInfoService.deleteSkuInfo(skuIds);
            // 4.删除spu的积分信息 TODO
            // 5.删除spu基本参数值
            pmsProductAttrValueService.deleteProductAttrValue(spuId);
            // 6.删除spu图片集
            pmsSpuImagesService.deleteSpuImages(spuId);
            // 7.删除spu商品介绍
            pmsSpuInfoDescService.delete(spuId);
            // 8.删除spu基本信息
            pmsSpuInfoRepository.delete(spuId);
            return spuId;
        }).collect(Collectors.toList());
        return collect.size();
    }

    /**
     * 查询商品详情
     */
    public Map getSpuDetail(Long spuId) {
        Map result = new HashMap();
        SpuInfoVo spuInfoVo = new SpuInfoVo();
        PmsSpuInfo spuInfo = findById(spuId);
        if (null == spuInfo) {
            throw new ServerException(ExceptionConstants.NOT_EXIST, "商品-" + spuId);
        }

        spuInfoVo.setId(spuInfo.getId());
        spuInfoVo.setBrandId(spuInfo.getBrandId());
        spuInfoVo.setCatalogId(spuInfo.getCatalogId());
        spuInfoVo.setCatalogPath(pmsCategoryService.findPathNameById(spuInfo.getCatalogId()));
        spuInfoVo.setSpuDescription(spuInfo.getSpuDescription());
        PmsSpuInfoDesc spuInfoDesc = pmsSpuInfoDescService.findById(spuId);
        spuInfoVo.setIntroduce(spuInfoDesc.getIntroduce());
        result.put("spuInfo", spuInfoVo);
        return result;
    }
}
